#include "VTTCommon.h"
#include "dirent.h"

#include <cstdlib>
#include <direct.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

using std::cout;
using std::endl;
using std::ifstream;
using std::string;
using std::stringstream;
using std::vector;

/**
  * Returns a boolean value indicating whether the argument corresponds to an
  * existing directory or not.
  *
  * @param strDir string to check whether it corresponds to an existing directory or not
  * @return boolean value indicating whether the argument corresponds to an existing directory or not
  */
bool IsDir( const string &strDir )
{	
	struct stat buffer;   
	return (stat( strDir.c_str(), &buffer ) == 0);
}

/**
  * Returns a boolean value indicating whether the argument corresponds to an
  * existing file or not.
  *
  * @param strFilename string to check whether it corresponds to an existing file or not
  * @return boolean value indicating whether the argument corresponds to an existing file or not
  */
bool IsFile( const string &strFilename )
{
	struct stat buffer;   
	return (stat( strFilename.c_str(), &buffer ) == 0);
}

/**
  * Returns a boolean value indicating whether the string in the argument is a valid
  * name for an image file or not. Only Jpegs and PNGs are valid image files.
  *
  * @param strFilename string to check whether it is a valid name for an image file or not
  * @return boolean value indicating whether the string in the argument is a valid name for an image file or not
  */
bool IsValidImageName( const string &strImageName )
{
	if (StringEndsWith( strImageName, ".jpg" ) ||
		StringEndsWith( strImageName, ".jpeg" ) ||
		StringEndsWith( strImageName, ".png" ))
	{
		return true;
	}

	return false;
}

/**
  * Generates a directory at the path that is given in the argument.
  * The direct root directory of the directory to generate must already exist.
  *
  * @param strDir path of the directory
  * @return boolean value indicating whether the generation of the directory was successful or not
  */
bool MakeDir( const string &strDir )
{
	return (_mkdir( strDir.c_str() ) == 0 ? true : false);
}

/**
  * Returns a vector with all Jpeg- and PNG-filenames inside the directory that is passed as an argument.
  *
  * @param strDir full path of the directory that is used for the image search. No directories inside this directory are considered.
  * @return vector with all Jpeg- and PNG-filenames inside the directory
  */
vector<string> GetAllImageNamesInsideDir( const string &strDir )
{
	vector<string> imageNames;

	if (!IsDir( strDir ))
		return imageNames;

	string str;
	DIR *p_dir;
	struct dirent *entry;

	if (p_dir = opendir( strDir.c_str() ))
	{
		while (entry = readdir( p_dir ))
		{
			if (strcmp( entry->d_name, "." ) != 0 && 
				strcmp( entry->d_name, ".." ) != 0 &&
				(StringEndsWith( entry->d_name, ".jpg" ) ||
				StringEndsWith( entry->d_name, ".jpeg" ) ||
				StringEndsWith( entry->d_name, ".png" )))
			{
				imageNames.push_back( entry->d_name );
			}
		}

		closedir( p_dir );
	}

	return imageNames;
}

/**
  * Returns the base path of the file, whose path is passed as an argument
  *
  * @param strFilename full path of the file
  * @return base path of the file or the passed string itself, when no base path can be found
  */
string GetBasePathOfFile( const string &strFilename )
{
	string::size_type index;

	index = strFilename.find_last_of( "/\\" );
	
	if (index == string::npos)
		return strFilename;
	else
		return strFilename.substr( 0, index );
}

/**
  * Returns a tm-struct that contains information about the changing date of the file
  * that corresponds to the path that is passed as an argument
  *
  * @param strFileName path to the file, whose changing date should be returned
  * @return tm-struct with the changing date of the file
  */
struct tm GetFileChangingDate( const string & strFileName )
{
	struct tm clock;
	struct stat attrib;
	stat( strFileName.c_str(), &attrib );

	gmtime_s( &clock, &(attrib.st_mtime) );

	return clock;
}

/**
  * Returns the file extension of the passed filename. For a file "image.jpg" this
  * would be "jpg", no dots are included. If no '.' is in strFilename, an empty
  * string is returned.
  *
  * @param strFilename filename or path
  * @return file extension of the passed filename
  */
string GetFileExtension( const string &strFilename )
{
	string::size_type index;

	index = strFilename.find_last_of( '.' );

	if (index == string::npos)
	{
		return "";
	}

	return strFilename.substr( index + 1 );
}

/**
  * Returs the file format ending for the passed image format.
  * For VTT_IMAGE_JPEG this is ".jpg".
  * For VTT_IMAGE_PNG this is ".png".
  * For every other argument an empty string is returned.
  *
  * @param imageFormat enum value corresponding to a specific image format
  * @return file format ending for the passed image format
  */
string GetFileFormatEnding( VTTImageFormat imageFormat )
{
	switch (imageFormat)
	{
		case VTT_IMAGE_JPEG:
			return ".jpg";

		case VTT_IMAGE_PNG:
			return ".png";

		default:
			return "";
	}
}

/**
  * Returns the filename that is part of the passed path.
  *
  * @param strFilename path of the file
  * @return filename that is part of the passed path
  */
string GetFileNameFromFullPath( const string &strFilename )
{
	return strFilename.substr( strFilename.find_last_of( "/\\" ) + 1);
}

/**
  * Returns the next power-of-two-number after number. Also when number is
  * power-of-two, the next possible power-of-two-number is returned.
  *
  * @param number number for which the next power-of-two-number is returned
  * @return next power-of-two-number after number
  */
UINT32 GetNextPowerOfTwo( UINT32 number )
{
	UINT32 n_pot = 1;

	while (n_pot <= number)
		n_pot *= 2;

	return n_pot;
}

/**
  * Returns the substring of stringToConsider, that contains all the characters after the first occurrence
  * of character. The search for character is started at startIndex, so the smallest index for the substring
  * to start from is startIndex + 1. All whitespaces are removed at the beginning and at the end of substring.
  * If stringToConsider does not contain the character, or if the first occurrence is at the last position, an
  * empty string is returned.
  *
  * @param stringToConsider string to consider for the substring-search
  * @param character character, that splits stringToConsider into the substring and the rest. character is never part of the substring
  * @param startIndex index that is used as a starting position for the character search
  * @return substring without whitespaces at the beginning or at the end
  */
string GetStrippedStringAfterChar( const string &stringToConsider, const char character, UINT32 startIndex )
{
	string::size_type index = stringToConsider.find( character, startIndex );
	string::size_type first_non_space_index;
	string::size_type last_non_space_index;

	if (index == string::npos)
		return "";

	first_non_space_index = stringToConsider.find_first_not_of( ' ', index + 1 );
	last_non_space_index = stringToConsider.find_last_not_of( ' ' );

	if (first_non_space_index == string::npos)
		return "";

	return stringToConsider.substr( first_non_space_index,
		last_non_space_index - first_non_space_index + 1 );
}

/**
  * Converts an std:string to a float value
  *
  * @param stringValue the string to convert to an int value
  * @return float-representation of the string
  */
float StdStringToFloat( const string &stringToConvert )
{
	stringstream string_stream( stringToConvert );
	float string_as_float;
	string_stream >> string_as_float;

	return string_as_float;
}

/**
  * Converts an std::string into an UINT32
  *
  * @param stringToConvert string to convert into an UINT32-value
  * @return UINT32-representation of the string
  */
UINT32 StdStringToUINT32( const string &stringToConvert )
{
	stringstream string_stream( stringToConvert );
	UINT32 string_as_uint32;
	string_stream >> string_as_uint32;

	return string_as_uint32;
}

/**
  *
  * Tests whether stringToConsider starts with startString or not.
  *
  * @param stringToConsider string to consider
  * @param startString start of the string that is searched at the beginning of stringToConsider
  * @return boolean value indicating whether the very first characters inside stringToConsider are startString or not
  */
bool StringStartsWith( const string &stringToConsider, const string &startString )
{
	string::size_type index = stringToConsider.find( startString );

	if (0 == index)
		return true;

	return false;
}

/**
  * Tests whether stringToConsider ends with endString or not.
  *
  * @param stringToConsider string to consider
  * @param endString end of the string that is searched at the end of stringToConsider
  * @return boolean value indicating whether the very last characters inside stringToConsider are endString or not
  */
bool StringEndsWith( const string &stringToConsider, const string &endString )
{
	string::size_type index = stringToConsider.find( endString );

	if (stringToConsider.size() >= endString.size() &&
		stringToConsider.size() - endString.size() == index)
		return true;

	return false;
}

/**
  * Converts an std:string to an int value
  *
  * @param stringValue the string to convert to an int value
  * @return int-representation of the string
  */
int StringToInt( const string &stringValue)
{
    std::stringstream ssStream(stringValue);
    int iReturn;
    ssStream >> iReturn;

    return iReturn;
}

/**
  * Converts an int value to an std:string
  *
  * @param iValue the int value to convert to an std::string
  * @return string-representation of the int value
  */
std::string UINT32ToString( const UINT32 &uint32Value)
{
    std::stringstream ssStream;
    ssStream << uint32Value;
    return ssStream.str();
}

/**
  * Converts a float value to an std:string
  *
  * @param fValue the float value to convert to an std::string
  * @return string-representation of the float value
  */
std::string FloatToString( const float &fValue)
{
    std::stringstream ssStream;
    ssStream << fValue;
    return ssStream.str();
}

/**
  * Tokenizes the passed string into parts and adds the parts into the passed vector.
  *
  * @param str string that should be tokenized
  * @param tokens vector for the tokens. the vector is not cleared, the found tokens are just added.
  * @param delimiters string of characters, where each of these characters is used as a delimiter between consecutive tokens
  */
void Tokenize( const string &str, vector<string> &tokens,
	const string &delimiters )
{
	// Skip delimiters at beginning.
	string::size_type lastPos = str.find_first_not_of(delimiters, 0);
	// Find first "non-delimiter".
	string::size_type pos     = str.find_first_of(delimiters, lastPos);

	while (string::npos != pos || string::npos != lastPos)
	{
		// Found a token, add it to the vector.
		tokens.push_back(str.substr(lastPos, pos - lastPos));
		// Skip delimiters.  Note the "not_of"
		lastPos = str.find_first_not_of(delimiters, pos);
		// Find next "non-delimiter"
		pos = str.find_first_of(delimiters, lastPos);
	}
}

/**
  * Overloading of the == operator to check whether two tm-structs are equal.
  * Two tm-structs are equal when all the int-fields are equal.
  *
  * @param time1 first time stamp
  * @param time2 second time stamp
  * @return boolean value indicating whether the two time stamps are equal or not
  */
bool operator ==( const struct tm &time1, const struct tm &time2 )
{
	return (time1.tm_hour == time2.tm_hour &&
		time1.tm_isdst == time2.tm_isdst &&
		time1.tm_mday == time2.tm_mday &&
		time1.tm_min == time2.tm_min &&
		time1.tm_mon == time2.tm_mon &&
		time1.tm_sec == time2.tm_sec &&
		time1.tm_wday == time2.tm_wday &&
		time1.tm_yday == time2.tm_yday &&
		time1.tm_year == time2.tm_year);
}